﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// CIELuv CHannel
    /// </summary>
    public enum CIELuvChannel
    {
        /// <summary>
        /// 
        /// </summary>
        L = 0,
        /// <summary>
        /// 
        /// </summary>
        u = 1,
        /// <summary>
        /// 
        /// </summary>
        v = 2
    }

    /// <summary>
    /// 
    /// </summary>
    [DebuggerDisplay("L: {m_L}, a: {m_u}, b: {m_v}")]
    public class CIELuv : AbstractColorSpace
    {
        protected double m_L = 0.0f;
        protected double m_u = 0.0f;
        protected double m_v = 0.0f;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_color"></param>
        public CIELuv(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CIELuv"/> class.
        /// </summary>
        /// <param name="_L">The _ L.</param>
        /// <param name="_u">The _u.</param>
        /// <param name="_v">The _v.</param>
        public CIELuv(double _L, double _u, double _v)
        {
            m_L = _L;
            m_u = _u;
            m_v = _v;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CIELuv"/> class.
        /// </summary>
        internal CIELuv()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CIELuv"/> class.
        /// </summary>
        /// <param name="_cieLuv">The _cie luv.</param>
        internal CIELuv(CIELuv _cieLuv)
        {
            this.m_Color = _cieLuv.m_Color;
            this.m_L = _cieLuv.m_L;
            this.m_u = _cieLuv.m_u;
            this.m_v = _cieLuv.m_v;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator CIELuv(Color _color)
        {
            return new CIELuv(_color);
        }

        /// <summary>
        /// Gets or sets L
        /// </summary>
        public double L
        {
            get { return m_L; }
            set { m_L = value; }
        }

        /// <summary>
        /// Gets or sets u
        /// </summary>
        public double u
        {
            get { return m_u; }
            set { m_u = value; }
        }

        /// <summary>
        /// Gets or sets v
        /// </summary>
        public double v
        {
            get { return m_v; }
            set { m_v = value; }
        }

        /// <summary>
        /// Calculates the transformation
        /// </summary>
        protected override void CalcValues()
        {
            CIEXYZ xyz = new CIEXYZ(m_Color);
            double var_U = ( 4 * xyz.X ) / ( xyz.X + ( 15 * xyz.Y ) + ( 3 * xyz.Z ) );
            double var_V = ( 9 * xyz.Y ) / ( xyz.X + ( 15 * xyz.Y ) + ( 3 * xyz.Z ) );

            double var_Y = xyz.Y / 100;
            if ( var_Y > 0.008856 ) var_Y = Math.Pow(var_Y, ( 1/3 ));
            else                    var_Y = ( 7.787 * var_Y ) + ( 16 / 116 );

            double ref_X =  95.047;        //Observer= 2°, Illuminant= D65
            double ref_Y = 100.000;
            double ref_Z = 108.883;

            double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );
            double ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );

            m_L = ( 116 * var_Y ) - 16;
            m_u = 13 * m_L * ( var_U - ref_U );
            m_v = 13 * m_L * ( var_V - ref_V );
        }

        /// <summary>
        /// back transformation to rgb
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            double var_Y = ( m_L + 16 ) / 116;
            if (Math.Pow(var_Y, 3) > 0.008856) var_Y = Math.Pow(var_Y, 3);
            else var_Y = (var_Y - 16 / 116) / 7.787;

            double ref_X =  95.047;     //Observer= 2°, Illuminant= D65
            double ref_Y = 100.000;
            double ref_Z = 108.883;

            double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );
            double ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) );

            double var_U = m_u / ( 13 * m_L ) + ref_U;
            double var_V = m_v / ( 13 * m_L ) + ref_V;

            double Y = var_Y * 100;
            double X =  - ( 9 * Y * var_U ) / ( ( var_U - 4 ) * var_V  - var_U * var_V );
            double Z = ( 9 * Y - ( 15 * var_V * Y ) - ( var_V * X ) ) / ( 3 * var_V );

            return new CIEXYZ(X,Y,Z).ToColor();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color L = {0}, u = {1}, v = {2}", m_L, m_u, m_v);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_L, m_u, m_v };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            m_L = (double)_aObject[0];
            m_u = (double)_aObject[1];
            m_v = (double)_aObject[2];
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new CIELuv(this);
        }
    }
}
